package server;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import shared.AccountData;
import shared.BalanceData;
import shared.CollectionData;
import shared.Data;
import shared.LoginData;
import shared.SharedData;

/**
 * class Connection controls the flow of the server for each user. Continues as
 * long as the user stay connected.
 * 
 * @author Rasmus Stjerne, Joachim Lindholm, Daniel Gramstrup, Pamela Pedersen
 * 
 */

public class Connection extends Thread {

	// constants corresponding to main menu options
	private static final int BALANCE = 1;
	private static final int SPEND = 2;
	private static final int INSERT = 3;
	private static final int LOGIN = 4;
	private static final int CCKMFACTOR = 5;
	private static final int CCUSEFACTOR = 6;
	private static final int NAME = 7;
	private static final int SEND_TRANSACTION = 8;
	private static final int PROCESS_TRANSACTIONS = 9;
	private static final int ACCEPT_TRANSACTION = 10;
	private static final int DECLINE_TRANSACTION = 11;
	private static final int TRANSACTION_LIMIT = 12;
	private static final int ACCOUNT_EXIST = 13;
	private static final int EXIT = -1;

	// Object input & output stream used in the TCP communication
	private ObjectInputStream in;
	private ObjectOutputStream out;

	// Socket object for each connection
	private Socket clientSocket;

	// Singleton implementation of the dccsDatabse
	private CCCSDatabase cccsDatabase = CCCSDatabase.getInstance();

	// Shared data objects
	private LoginData ld;
	private CollectionData cd;
	private BalanceData bd;
	private AccountData ad;
	private SharedData sd;

	// Used to store the current account inside the unique connection
	private String currentAccountName = null;

	// Control whether the user selected to exit
	private boolean userExited = false;

	/**
	 * Constructor for the connection class
	 * 
	 * @param aClientSocket
	 *            supplied as argument
	 */
	public Connection(Socket aClientSocket) {

		try {
			clientSocket = aClientSocket;
			in = new ObjectInputStream(clientSocket.getInputStream());
			out = new ObjectOutputStream(clientSocket.getOutputStream());
			this.start();
		} catch (IOException e) {
			System.out.println("Connection:" + e.getMessage());
		}
	}

	/**
	 * Method to control the flow of the connection. This method persist to run
	 * until the user exits
	 */
	public void run() {
		try {
			while (!userExited) {

				// receive SharedData object over TCP
				sd = (SharedData) in.readObject();

				// Based on the desired action selected by the user the switch
				// decides how to proceed
				switch (Data.xor(sd.transactionID)) {
				// Complete balance overview
				case BALANCE:
					bd = cccsDatabase.getBalance(this.currentAccountName);
					bd.balance = Data.xor(bd.balance);
					bd.pendingBalance = Data.xor(bd.pendingBalance);
					bd.totalBalance = Data.xor(bd.totalBalance);
					out.writeObject(bd);
					break;
				// Limitation for how much a specific user can transfer
				case TRANSACTION_LIMIT:
					double transactionLimit = cccsDatabase
							.getTransactionLimit(currentAccountName);
					out.writeObject(Data.xor(transactionLimit));
					break;
				// Spend CC based on an amount supplied by the user
				case SPEND:
					cccsDatabase.spend(this.currentAccountName,
							Data.xor(sd.amount));
					break;
				// Insert CC based on an amount supplied by the user
				case INSERT:
					cccsDatabase.insert(this.currentAccountName,
							Data.xor(sd.amount));
					break;
				// User login
				case LOGIN:
					ld = new LoginData();
					this.currentAccountName = Data.xor(sd.accountName);
					ld.userAuthenticated = cccsDatabase.authenticateUser(
							this.currentAccountName,
							Data.xor(sd.accountPassword));
					out.writeObject(ld);
					break;
				// Distribute the static CCKMFACTOR value to the user
				case CCKMFACTOR:
					out.writeObject(Data.xor(Account.CCKMFACTOR));
					break;
				// Calculate the dynamic CCUSERFACTOR value to the user
				case CCUSEFACTOR:
					out.writeObject(Data.xor(Parser.computeCCUSEFACTOR()));
					break;
				// Extract the current account name
				case NAME:
					out.writeObject(Data.xor(cccsDatabase.getName(Data
							.xor(sd.accountName))));
					break;
				// Send transaction between two unique Accounts
				case SEND_TRANSACTION:
					cccsDatabase.addTransaction(Data.xor(sd.recipient),
							this.currentAccountName,
							Data.xor(sd.transferAmount));
					break;
				// Compile a list of all pending transactions and the send it to
				// the user
				case PROCESS_TRANSACTIONS:
					cd = cccsDatabase
							.getPendingTransactions(this.currentAccountName);
					cd.xorArraylist();
					out.writeObject(cd);
					break;
				// Accept a specific transaction based on a selection made by
				// the user
				case ACCEPT_TRANSACTION:
					cccsDatabase.transfer(Data.xor(sd.selection),
							this.currentAccountName);
					break;
				// Decline a specific transaction based on a selection made by
				// the user
				case DECLINE_TRANSACTION:
					cccsDatabase.decline(Data.xor(sd.selection),
							this.currentAccountName);
					break;
				// User exit
				case EXIT:
					userExited = true;
					cccsDatabase.logout(this.currentAccountName);
					break;
				// Control whether the transaction recipient exist in the
				// database
				case ACCOUNT_EXIST:
					ad = new AccountData();
					ad.userExists = cccsDatabase.checkAccountExists(Data
							.xor(sd.recipient));
					out.writeObject(ad);
					break;
				default:
					userExited = true;
					break;

				}
			}
		} catch (EOFException e) {
			if (sd == null) {
				System.err.println("Connection tested");
			} else {
				System.out.println("EOF:" + e.getMessage());
			}
		} catch (IOException e) {
			// In the event of IOException the current user with this specific
			// connection is logged off.
			userExited = true;
			cccsDatabase.logout(this.currentAccountName);
			System.err.println("IO:" + e.getMessage());

		} catch (ClassNotFoundException e) {
			System.out.println("CNF: " + e.getMessage());
		} finally {
			try {
				// After the user exits kill the current connection
				clientSocket.close();
				System.err.println("Closed connection");
			} catch (IOException e) {
				System.out.println("Failed to close connection!");
			}
		}
	}

}